package com.example.opcdemo.opc;


import com.example.opcdemo.opc.config.OpcUaConfig;
import com.google.common.collect.Lists;

import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.UaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

/**
 * ua opc服务impl
 *
 * @author Administrator
 * @date 2023/06/13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UaOpcServiceImpl  {

    private final OpcUaConfig opcUaConfig;


    public Map<String, Object> readValue(BizOpcInfo bizOpcInfo) {
        Map<String, Object> map = Maps.newHashMap();
//        BizOpcInfo bizOpcInfo = new BizOpcInfo();
//        bizOpcInfo.setOpcUrl("opc.tcp://192.168.3.82:8098/TM.LongWallMind.OPCUAServer4.x");
//        bizOpcInfo.setSurfaceNum("039");
//        bizOpcInfo.setKeyType1("ns=2;s=OPC.电液控.支架集.#.立柱压力.前柱压力");
//        bizOpcInfo.setKeyType2("ns=2;s=OPC.电液控.支架集.{0}.立柱压力.后柱压力");
        opcUaConfig.createSubscription(bizOpcInfo, true);
        if (null == OpcUaConfig.opcLink ){
            log.error("连接失败");
            return map;
        }
        BizWorksupportPoint point = new BizWorksupportPoint();
        point.setSupportNum(40L);
        log.info("读值开始==============》");
        Boolean valueFlag = readValue(OpcUaConfig.opcLink, point, bizOpcInfo, map);
        return map;
    }





    /**
     * 读取数据 存到redis
     *
     * @param uaClient   代理客户端
     * @param point      点
     * @param bizOpcInfo 商业opc信息
     * @return {@link Boolean}
     */
    private Boolean readValue(UaClient uaClient, BizWorksupportPoint point, BizOpcInfo bizOpcInfo, Map<String, Object> map ) {
        // 默认失败
        boolean flag = false;
        try {
            // 获取综采支架号
            Long supportNum = bizOpcInfo.getSupportNum();
            String equipmentType = point.getEquipmentType();
            if (supportNum != null && bizOpcInfo != null && uaClient != null) {
                String fileName = "keyType%s";
                // 循环测点通道类型
                for (int i = 1; i <= Integer.parseInt(equipmentType); i++) {
                    Object dataValue = getTempDataValue(uaClient, bizOpcInfo, supportNum, fileName, i);
                    // 值不为空 存到缓存中
                    if (null != dataValue) {
                        flag = true;
                        // 将读取的值存到opc值集合
                        map.put(point.getMonitorPointNum() + i, dataValue);

                    } else {
                        flag = false;
                        // 失败的存储-1
                        map.put(point.getMonitorPointNum()+ i, -1);
                    }
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 获取 指定通道opc的值
     *
     * @param uaClient   ocp客户端
     * @param bizOpcInfo opc信息
     * @param supportNum 支架号
     * @param fileName   字段名称
     * @param i          我
     * @return {@link Object}
     * @throws InterruptedException 中断异常
     * @throws ExecutionException   执行异常
     */
    private Object getTempDataValue(UaClient uaClient, BizOpcInfo bizOpcInfo, Long supportNum, String fileName, int i) throws InterruptedException, ExecutionException {
        NodeId nodeId;
        DataValue value;
        // 获取通道配置的参数 通道字段
        Object fieldValue = ReflectUtils.getFieldValue(bizOpcInfo, String.format(fileName, i));
        if (null != fieldValue) {
            String keyTypeValue = (String) fieldValue;
            log.info("读取到的标识符 {}", keyTypeValue);
            // 拼接支架 通过标识读取数据
            String dataKey = MessageFormat.format(keyTypeValue, MyStringUtil.addZeroForNum(String.valueOf(supportNum), 3));
            nodeId = new NodeId(2, dataKey);
            value = uaClient.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();
            return value.getValue().getValue();
        }
        return null;
    }



    /**
     * 创建OPC UA客户端
     * @return
     * @throws Exception
     */
    public OpcUaClient createClient(String endPointUrl) throws Exception {
        //opc ua服务端地址
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }
        return OpcUaClient.create(endPointUrl, endpoints ->
                        endpoints.stream()
                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
                                .setApplicationUri("urn:eclipse:milo:examples:client")
                                //访问方式
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
    }


    /**
     * 创建OPC UA客户端
     * @return
     * @throws Exception
     */
    public OpcUaClient createClient1(String endPointUrl) throws Exception {
        //opc ua服务端地址
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }
        return OpcUaClient.create(endPointUrl,
                endpoints -> {
                    final Optional<EndpointDescription> endpoint = endpoints
                            .stream()
                            .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                            .findFirst();
                    EndpointDescription newEndpoint=new EndpointDescription(endPointUrl, endpoint.get().getServer(), endpoint.get().getServerCertificate(),
                            endpoint.get().getSecurityMode(),endpoint.get().getSecurityPolicyUri(),endpoint.get().getUserIdentityTokens(),
                            endpoint.get().getTransportProfileUri(), endpoint.get().getSecurityLevel());
                    return Optional.of(newEndpoint);
                },
                configBuilder -> configBuilder
                        .setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
                        .setApplicationUri("urn:eclipse:milo:examples:client")
                        //访问方式
                        .setIdentityProvider(new AnonymousProvider())
                        .setRequestTimeout(UInteger.valueOf(60000))
                        .build()
        );
    }
}
